home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / swtools / malloc_db / malloc_db.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  16.8 KB  |  634 lines

  1. /*
  2.  * malloc_db.c
  3.  *
  4.  *    Malloc heap trashing debugger.
  5.  *
  6.  *                Written by Gianni Mariani some time in 1990
  7.  *                Updated recently to provide lots of comments.
  8.  *
  9.  *  This code may be distributed freely on the following 5 conditions :
  10.  *    1. No money or favor is exchanged for this code.
  11.  *    2. This notice remains part of this code.
  12.  *    3. You do not actively hinder its distribution.
  13.  *    4. Credit is given to the author.
  14.  *    5. Bug fixes or enhancements are passed on based on these
  15.  *       conditions.
  16.  *
  17.  *  IRIX is a trademark of Silicon Graphics Inc, CA.
  18.  *
  19.  *
  20.  *  This module provides all the malloc() memory allocation routines
  21.  *  that is provided in the c library. In addition if db_check_on is
  22.  *  true, the entire arena is scanned on every free() malloc() or realloc()
  23.  *  call.   (in dbx, execute "assign db_check_on=1".   in cvd, go into the
  24.  *  "Expression View" screen from the "Views" pulldown menu, and type 
  25.  *  "db_check(1)" in the "Expression:" column followed by a carriage return;
  26.  *  when db_check(1) is executed successfully (i.e. the memory arena has not
  27.  *  yet been trashed and the test performed by "db_check(1)" returned 
  28.  *  successfully) there will appear something like a 9-digit number in the
  29.  *  corresponding "Result:" column that is confirmation that db_check() did
  30.  *  not fail.)  db_check( 1 ) can also be called from the debugger or other
  31.  *  interesting functions to increase the frequency that the arena is
  32.  *  checked. 
  33.  *
  34.  *  db_check( 1 ) checks the arena for writes past the end of allocated 
  35.  *  memory or before allocated memory, when it finds an inconsistancy it will
  36.  *  purposly write to location 0 to core dump.  It will set db_bugptr1 thru
  37.  *  db_bugptr4 and db_lastgood to interesting pointers.  The purpose is so 
  38.  *  that once the pointer in error is found, one can set a watch point (from
  39.  *  within cvd) on it and find the culprit code !  db_bugptr3 and db_bugptr4
  40.  *  are usually the addresses that are actually probably corrupted.
  41.  *
  42.  *  ->->-> NOTE: YOU MUST "% setenv DB_CHECK ON" TO ACTIVATE IT. <-<-<-
  43.  *
  44.  *  Example scenario :
  45.  *      1. app core dumps in malloc(<stripped>) somewhere - bummed.
  46.  *      2. relink with -g3 malloc_db.c and -lmpc
  47.  *      3. run the app.
  48.  *        set environment variable
  49.  *        -- setenv DB_CHECK ON  
  50.  *          cross fingers - yes it core dumped in malloc_db.c
  51.  *            ( gotcha - I feel like a fosters { fozzie's for ozzies } )
  52.  *        --    this bit might take oodles (lots) of time, you might
  53.  *        want to get real swift and use the debugger to switch
  54.  *        off db_checking by setting db_check_on = 0 and later
  55.  *        setting it on db_check_on = 1 when you know you're
  56.  *        near the bug.
  57.  *      4. make note of the addresses of db_bugptr4 and db_bugptr3 by:
  58.  *        -- looking at stdout in the shell window you ran the app from,
  59.  *        -- from within cvd, using the "Expression View" window
  60.  *        -- from within dbx, executing "print db_bugptr3,db_bugptr4"
  61.  *      5. set watch points on these addrs
  62.  *        These addresses are probably not in the address space yet -
  63.  *        this means you have to wait until the address is alloacted by
  64.  *        the o.s. (sbrk() usually).  To get around this, the first call
  65.  *        to db_check( 1 ) will allocate and free DB_CHUNKSIZE of memory
  66.  *        ( you might need to adjust DB_CHUNKSIZE ) so that it brings
  67.  *        those addresses into the address space of the process.
  68.  *          to set a watch point in cvd for db_bugptr3 and db_bugptr4 (call
  69.  *          them 0x100d3628 and 0x100d3854 respectively) type in at the 
  70.  *          "cvd>" prompt:  "watch addr 0x100d3628" and on at the next
  71.  *          prompt: "watch addr 0x100d3854".  by default execution will 
  72.  *          stop when this address is next written to.
  73.  *      6. restart the app.
  74.  *          cross fingers - hopefully cvd will break on one of these 2 watch 
  75.  *          points. If you get stopped with some kind of message like 
  76.  *
  77.  *  [1] Process 4406 stopped at ["malloc_db.c":488, 0x400810
  78.  *
  79.  *          watch break inside of malloc_db.c, it usually means that 
  80.  *        malloc_db.c is setting up its own db_malsort memory structs - 
  81.  *          ignore 'em and execute "continue" to step past the initializing
  82.  *          "setup" calls from within malloc_db.c.  if it seems like you're 
  83.  *          having to press "continue" too much, try telling cvd to stop in
  84.  *          something else that is related to manipulating memory like read:
  85.  *
  86.  *  cvd> stop in read
  87.  *  [2] Stop entry read file sys/read.s
  88.  *  cvd> rerun
  89.  *  [2] Process 4413# stopped at ["read.s":14, 0x4f39c0]
  90.  *  cvd> c
  91.  *  [2] Process 4413 stopped at ["read.s":14, 0x4f39c0]
  92.  *  cvd> status
  93.  *  [0] Stop watch address 0x100d3628 size 4 for write access
  94.  *  [1] Stop watch address 0x100d3854 size 4 for write access
  95.  *  [2] Stop entry read file sys/read.s
  96.  *  cvd> c
  97.  *  [2] Process 4413 stopped at ["read.s":14, 0x4f39c0]
  98.  *  cvd> c
  99.  *  [2] Process 4413 stopped at ["read.s":14, 0x4f39c0]
  100.  *  cvd> c
  101.  *
  102.  *          If it seems that you are having to hit continue too much with
  103.  *          "read" as well, do something like:
  104.  *
  105.  *  cvd> return
  106.  *  Process 4413 stopped at ["XlibInt.c":249, 0x4b2f10]
  107.  *  cvd> c
  108.  *  [2] Process 4413 stopped at ["read.s":14, 0x4f39c0]
  109.  *  cvd> disable 2
  110.  *  cvd> status
  111.  *  [0] Stop watch address 0x100d3628 size 4 for write access
  112.  *  [1] Stop watch address 0x100d3854 size 4 for write access
  113.  *  [2] Stop entry read file sys/read.s[inactive]
  114.  *  cvd> c
  115.  *  Process 4413 stopped at ["malloc_db.c":485, 0x4007f8]
  116.  *  cvd> c
  117.  *  cvd> return
  118.  *
  119.  *          at this point, either you are finding yourself stopping at some
  120.  *          sort of "interesting" segment in your own code somewhere, OR you
  121.  *          continue to find yourself stopping inside malloc_db.c.  if it
  122.  *          is the former, run db_check(1) from the "Expression Views" window
  123.  *          (to "turn off" db_check() at any point, do something like moving 
  124.  *          the cursor over the final parenthesis, delete it, and then press
  125.  *          carriage return.  this will generate a "<syntax error>" in the 
  126.  *          "Results:" column on the same line where the 9-digit integer used
  127.  *          to be--the effect of this is to disable db_check().)  if it fails 
  128.  *          then !!! the culprit code is found !!!  (if not you need to 
  129.  *          continue trying to blindly approaching the invisible segment of 
  130.  *          your own code where you are mysteriously trashing/walking over
  131.  *          memory without your conscious knowledge.)
  132.  *        Invoke AI bug fixer.
  133.  *        89689655% AI_bug_fix <file.c> <line num>
  134.  *        I'm realeasing AI_bug_fix(1C) some time back to the future !
  135.  *        -- just kidding !
  136.  *
  137.  *  Notes:
  138.  *
  139.  *  If lots of memory elements are allocated it will consume lots of
  140.  *  cpu time in db_check().
  141.  *
  142.  *  I've found it most helpfull - stomped the yukkiest of critters.
  143.  *
  144.  *  Enjoy bug swatting ---!
  145.  *
  146.  *  DISCLOSURE: No warranty - specified or otherwise implied is given
  147.  *  on the fitness of this code for anything.  In other words - you take
  148.  *  it - you own the bugs. !
  149.  *
  150.  *  You're welcome to send me ( gianni@neu.sgi.com ) email but I might
  151.  *  ignore them.
  152.  *
  153.  *  Linking - You may need to link it with -lmpc
  154.  *
  155.  *  BUGS/RFE's :-
  156.  *    1. there may be up to a three byte gap between the allocated
  157.  *       memory and the address that is checked for overwrite.  This
  158.  *       can be fixed by using a byte by byte comparison routine.
  159.  *    2. The control structures used by amalloc() are not verified
  160.  *       so if these are overwritten db_check() will not find it.
  161.  *    3. The real amalloc should provide the information this does
  162.  *       when amallopt( M_DEBUG, 1 ) is set.
  163.  *    4. This only works on IRIX systems that have amalloc(3P).
  164.  */
  165.  
  166. #include    <stdio.h>
  167. #include    <malloc.h>
  168.  
  169. /* ======== db_malsort ================================================ */
  170. /* PURPOSE:
  171.  *    Malloc debugging structure.
  172.  */
  173.  
  174. typedef struct  db_malsort {
  175.     long          db_id;        /* Debugger ID        */
  176.     struct db_malsort    * db_links[ 2 ];    /* Links to others        */
  177.     long          db_rsize;        /* requested size of mem    */
  178.     long          db_size;        /* size of mem        */
  179. } db_malsort;
  180.  
  181. #define    DB_IDVAL    0xdf3e55aa
  182.  
  183. extern db_malsort  db_headlink[];
  184.  
  185. db_malsort  db_headlink[ 2 ] = {
  186.     { DB_IDVAL, { db_headlink, db_headlink }, 0, 0 },
  187.     { DB_IDVAL, { db_headlink, db_headlink }, 0, 0 },
  188. };
  189.  
  190. int          db_check_on = -1;        /* Check status flag.        */
  191. char        * db_coredump = 0;        /* Easy way to core dump        */
  192. db_malsort  * db_bugptr1 = 0;        /* Pointer that we crap up on    */
  193. db_malsort  * db_bugptr2 = 0;        /* Pointer that we crap up on    */
  194. long        * db_bugptr3 = 0;        /* Pointer that we crap up on       */
  195. long        * db_bugptr4 = 0;        /* Pointer that we crap up on       */
  196.  
  197. db_malsort  * db_lastgood = 0;        /* Pointer that last checked out    */
  198.  
  199. unsigned long db_sizeit = ~0;
  200.  
  201. int          dbm_extend_size = 0x10000;
  202.  
  203. extern void * sbrk();
  204.       
  205. void    * dbm_arena;
  206.  
  207. #define DB_ALIGN( osize )   ( ( ( osize - 1 ) | 3 ) + 1 )
  208.  
  209. /* ======== dbm_getarena ============================================== */
  210. /* PURPOSE:
  211.  *    Get the arena pointer
  212.  *
  213.  * RETURNS:
  214.  *     Arena pointer
  215.  */
  216.  
  217. void    * dbm_getarena()
  218. {
  219.     if ( ! dbm_arena ) {
  220.     dbm_arena = acreate(
  221.         sbrk( dbm_extend_size ),
  222.         dbm_extend_size,
  223.         0,
  224.         0,
  225.         sbrk
  226.     );
  227.     }
  228.     return dbm_arena;
  229. }
  230.  
  231. /* ======== db_alloc_chunk ============================================ */
  232. /* PURPOSE:
  233.  *    Allocate a large chunk and free it - This is so that it gets
  234.  *  placed in process space so debuggers don't complain.
  235.  *
  236.  * RETURNS:
  237.  *     NOthing.
  238.  */
  239.  
  240. #define DB_CHUNKSIZE    0x40000        /* allocate 256 K            */
  241.  
  242. db_alloc_chunk()
  243. {
  244.     char    * mem;
  245.     
  246.     mem = malloc( DB_CHUNKSIZE );
  247.  
  248.     if ( mem ) {
  249.     free( mem );
  250.     }
  251.  
  252.     return;
  253.  
  254. } /* end db_alloc_chunk */
  255.  
  256.  
  257. /* ======== db_printem ================================================ */
  258. /* PURPOSE:
  259.  *    Print some stuff before we go down - just in case we dont get core
  260.  *
  261.  * RETURNS:
  262.  *     yes it does!
  263.  */
  264.  
  265. void    db_printem( char * where, int line )
  266. {
  267.  
  268.     printf( "%s:%d found heap trashing\n", __FILE__, line );
  269.     printf( "    diagnotic : %s\n", where );
  270.     printf( "db_bugptr1 = 0x%08x\n", db_bugptr1 );
  271.     printf( "db_bugptr2 = 0x%08x\n", db_bugptr2 );
  272.     printf( "db_bugptr3 = 0x%08x\n", db_bugptr3 );
  273.     printf( "db_bugptr4 = 0x%08x\n", db_bugptr4 );
  274.     printf( "->>>dumping core\n" );
  275.     fflush( stdout );
  276.  
  277.     return;
  278.  
  279. } /* end db_printem */
  280.  
  281.  
  282. /* ======== db_check ================================================== */
  283. /* PURPOSE:
  284.  *    Check the arena for failure.
  285.  *
  286.  * RETURNS:
  287.  *     Nothing when ok, does not return on failure.
  288.  */
  289.  
  290. db_check( int doitanyway )
  291. {
  292.     db_malsort        * ptr;
  293.     char        * getenv();
  294.     char        * str;
  295.     
  296.     /* Check to see if debugger is reqired.                */
  297.     if ( db_check_on == -1 ) {
  298.  
  299.     if ( str = getenv( "DB_CHECK" ) ) {
  300.         db_check_on = ! strcmp( str, "ON" );
  301.     } else {
  302.         db_check_on = 0;
  303.     }
  304.     
  305.     if ( str = getenv( "DB_CHECK_SIZE" ) ) {
  306.         db_sizeit = atoi( str );
  307.     }
  308.  
  309.     db_alloc_chunk();
  310.     }
  311.  
  312.     if ( db_check_on || doitanyway ) {
  313.     /* Do memory arena consistency check.                */
  314.     
  315.     ptr = db_headlink;
  316.     db_lastgood = 0;
  317.     
  318.     do {
  319.  
  320.         /* if written before allocated memory            */
  321.         if ( ptr->db_size != DB_ALIGN( ptr->db_rsize ) ) {
  322.         db_bugptr1 = ptr;
  323.         db_bugptr3 = ( long * ) &( ptr->db_rsize );
  324.         db_bugptr4 = ( long * ) &( ptr->db_size );
  325.         db_printem( "(before allocated mem 1)", __LINE__ );
  326.         * db_coredump = 0;      /* Core dump!                   */
  327.         exit( 4 );
  328.         }
  329.         
  330.         /* if writing over the end of allocated memory...        */
  331.         if (
  332.             * ( long * )
  333.             ( ( char * ) ptr + ( ptr->db_size ) + sizeof( * ptr ) ) !=
  334.         DB_IDVAL
  335.         ) {
  336.         db_bugptr3 = ( long * )
  337.             ( ( char * ) ptr + ( ptr->db_size ) + sizeof( * ptr ) )
  338.         ;
  339.         db_bugptr1 = ptr;
  340.         db_printem( "(after alloacted mem 2)", __LINE__ );
  341.         * db_coredump = 0;    /* Core dump!            */
  342.         exit( 4 );
  343.         }
  344.  
  345.         /* Test the ID                        */
  346.         if ( ptr->db_id != DB_IDVAL ) {
  347.         db_bugptr3 = ( long * ) & ( ptr->db_id );
  348.         db_bugptr1 = ptr;
  349.         db_printem( "(unsure - probably before 3)", __LINE__ );
  350.         * db_coredump = 0;    /* Core dump!            */
  351.         exit( 1 );
  352.         }
  353.  
  354.         db_bugptr2 = ptr->db_links[ 0 ];
  355.         /* Test the back and foreward links                */
  356.         if ( ptr->db_links[ 0 ]->db_links[ 1 ] != ptr ) {
  357.         db_bugptr1 = ptr;
  358.         db_bugptr2 = ptr->db_links[ 0 ];
  359.         db_bugptr3 = ( long * ) &( ptr->db_links[ 0 ] );
  360.         db_bugptr4 = ( long * ) &( ptr->db_links[ 0 ]->db_links[ 1 ] );
  361.         db_printem( "(unsure - forward/back link wrong 4)", __LINE__ );
  362.         * db_coredump = 0;    /* Core dump!            */
  363.         exit( 2 );
  364.         }
  365.  
  366.         db_bugptr2 = ptr->db_links[ 1 ];
  367.         /* Test the forward and back links.                */
  368.         if ( ptr->db_links[ 1 ]->db_links[ 0 ] != ptr ) {
  369.         db_bugptr1 = ptr;
  370.         db_bugptr2 = ptr->db_links[ 1 ];
  371.         db_bugptr3 = ( long * ) &( ptr->db_links[ 1 ] );
  372.         db_bugptr4 = ( long * ) &( ptr->db_links[ 1 ]->db_links[ 0 ] );
  373.         db_printem( "(unsure - back/forward link wrong 5)", __LINE__ );
  374.         * db_coredump = 0;    /* Core dump!            */
  375.         exit( 2 );
  376.         }
  377.         db_bugptr2 = 0;
  378.  
  379.         db_lastgood = ptr;
  380.         ptr = ptr->db_links[ 1 ];
  381.         
  382.     } while ( ptr != db_headlink );
  383.     }
  384.  
  385.     return;
  386.  
  387. } /* end db_check */
  388.  
  389. /* ======== db_break ================================================== */
  390. /* PURPOSE:
  391.  *    Break point address when memory of size >= db_sizeit requested.
  392.  *    Provides an interesting breakpoint address.
  393.  *
  394.  * RETURNS:
  395.  *     Nothing.
  396.  */
  397.  
  398. void    db_break( ptr )
  399. void        * ptr;
  400. {
  401.  
  402.     /* memory of size >= db_sizeit requested.  Do something interesting    */
  403.     return;
  404.  
  405. } /* end db_break */
  406.  
  407.  
  408. /* ======== malloc ================================================= */
  409. /* PURPOSE:
  410.  *    malloc entry point for malloc debugger.
  411.  *
  412.  * RETURNS:
  413.  *     Pointer to allocated memory.
  414.  */
  415.  
  416. void     * malloc( size_t osize )
  417. {
  418.     db_malsort        * ptr;
  419.     int              size;
  420.     
  421.     size = DB_ALIGN( osize );
  422.     
  423.     db_check( 0 );
  424.     
  425.     ptr = ( db_malsort * ) amalloc(
  426.     size + sizeof( * ptr ) + sizeof( long ),
  427.     dbm_getarena()
  428.     );
  429.     
  430.     if ( ptr ) {
  431.     ptr->db_rsize = osize;
  432.     ptr->db_size = size;
  433.     
  434.     db_linkit( ptr );
  435.  
  436.     if ( size >= db_sizeit ) {
  437.         db_break(
  438.         (long *) ( ( (char *) ptr ) + (ptr->db_size) + sizeof(* ptr) )
  439.         );
  440.     }
  441.     
  442.     return ( char * ) ( ptr + 1 );
  443.     } else {
  444.     
  445.     return ( char * ) 0;
  446.     }
  447.  
  448. } /* end malloc */
  449.  
  450.  
  451. /* ======== realloc ================================================ */
  452. /* PURPOSE:
  453.  *    Reallocate
  454.  *
  455.  * RETURNS:
  456.  *     
  457.  */
  458.  
  459. void    * realloc( void * vptr, size_t osize )
  460. {
  461.     db_malsort  * ptr = vptr;
  462.     int          size;
  463.     db_check( 0 );
  464.  
  465.     size = DB_ALIGN( osize );
  466.     
  467.     ptr --;
  468.     db_unlink( ptr );
  469.  
  470.     ptr = ( db_malsort * )
  471.     arealloc( ptr, size + sizeof( * ptr ) + sizeof( long ), dbm_getarena() )
  472.     ;
  473.  
  474.     if ( ptr ) {
  475.     ptr->db_size = size;
  476.     ptr->db_rsize = osize;
  477.     
  478.     db_linkit( ptr );
  479.     
  480.     return ( char * ) ( ptr + 1 );
  481.     } else {
  482.     return ( char * ) 0;
  483.     }
  484.     
  485. } /* end realloc */
  486.  
  487.  
  488. /* ======== free =================================================== */
  489. /* PURPOSE:
  490.  *    Free entry point.
  491.  *
  492.  * RETURNS:
  493.  *     Nothing.
  494.  */
  495.  
  496. void free( void * vptr )
  497. {
  498.     db_malsort  * ptr = ( db_malsort * ) vptr;
  499.     ptr -= 1;
  500.  
  501.     db_check( 0 );
  502.  
  503.     db_unlink( ptr );
  504.     
  505.     afree( ptr, dbm_getarena() );
  506.  
  507.     return;
  508.  
  509. } /* end free */
  510.  
  511.  
  512. /* ======== db_unlink ================================================= */
  513. /* PURPOSE:
  514.  *    Unlink pointer from the list.
  515.  *
  516.  * RETURNS:
  517.  *     
  518.  */
  519.  
  520. db_unlink( ptr )
  521. db_malsort  * ptr;
  522. {
  523.     
  524.     ptr->db_links[ 0 ]->db_links[ 1 ] = ptr->db_links[ 1 ];
  525.     ptr->db_links[ 1 ]->db_links[ 0 ] = ptr->db_links[ 0 ];
  526.     
  527.     return;
  528.  
  529. } /* end db_unlink */
  530.  
  531.  
  532. /* ======== db_linkit ================================================= */
  533. /* PURPOSE:
  534.  *    link a pointer into the list.
  535.  *
  536.  * RETURNS:
  537.  *     
  538.  */
  539.  
  540. db_linkit( ptr )
  541. db_malsort  * ptr;
  542. {
  543.     
  544.     /* Place the ID                            */
  545.     ptr->db_id = DB_IDVAL;
  546.     
  547.     ptr->db_links[ 0 ] = db_headlink->db_links[ 0 ];
  548.     ptr->db_links[ 1 ] = db_headlink;
  549.  
  550.     db_headlink->db_links[ 0 ] = ptr;
  551.     ptr->db_links[ 0 ]->db_links[ 1 ] = ptr;
  552.     
  553.     * ( long * ) ( ( char * ) ptr + ( ptr->db_size ) + sizeof( * ptr ) ) =
  554.     DB_IDVAL
  555.     ;
  556.     
  557.     return;
  558.  
  559. } /* end db_linkit */
  560.  
  561.  
  562. /* ======== calloc ================================================= */
  563. /* PURPOSE:
  564.  *    Calloc debugger entry point
  565.  *
  566.  * RETURNS:
  567.  *     
  568.  */
  569.  
  570. void    * calloc( size_t nelem, size_t size )
  571. {
  572.     char    * p;
  573.     int          len;
  574.     
  575.     p = malloc( len = nelem * size );
  576.  
  577.     if ( p ) {
  578.     memset( p, 0, len );
  579.     }
  580.  
  581.     return p;
  582.  
  583. } /* end calloc */
  584.  
  585.  
  586. int        mallopt( int1, int2 )
  587. int        int1;
  588. int        int2;
  589. {
  590.     return amallopt( int1, int2, dbm_getarena() );
  591. }
  592.  
  593. struct mallinfo    mallinfo()
  594. {
  595.     return amallinfo( dbm_getarena() );
  596. }
  597.  
  598. #if TEST
  599. int          i;
  600.  
  601. main()
  602. {
  603.  
  604.     char    * memory[ 100 ];
  605.     
  606.     for ( i = 0; i < 100; i ++ ) {
  607.     
  608.     memory[ i ] = malloc( i );
  609.  
  610.     db_check_on = 1;
  611.     }
  612.  
  613.     for ( i = 0; i < 50; i++ ) {
  614.     
  615.     free( memory[ i * 2 ] );
  616.  
  617.     }
  618.     
  619.     printf( "OK so far - lets detect one ! %x !\n", ( memory[ 49 ] - 2 ) );
  620.  
  621.     * ( memory[ 49 ] - 2 ) = 0;
  622.     * ( memory[ 49 ] - 10 ) = 0;
  623.     
  624.     for ( i = 0; i < 50; i++ ) {
  625.  
  626.     printf( "%d\n", i );
  627.     free( memory[ 1 + i * 2 ] );
  628.  
  629.     }
  630.  
  631. }
  632.  
  633. #endif
  634.